(define (null obj)
  (eqv? obj '()))

(define (not obj) (if obj #f #t))

(define (id obj) obj)

(define (curry f arg0)
  (lambda (arg1) (f arg0 arg1)))

(define (compose f g)
   (lambda (arg) (f (g arg))))

(define null? null)

(define (list . elems) elems)

(define zero? (curry = 0))

(define positive? (curry > 0))

(define negative? (curry < 0))

(define (odd? x)
  (= (mod x 2) 1))

(define (even? x)
  (= (mod x 2) 0))

(define (reduce f acc lst)
  (if (null? lst)
      acc
      (reduce f (f acc (car lst)) (cdr lst))))

(define (unfold f elt pred)
  (if (pred elt)
      (cons elt '())
      (cons elt (unfold f (f elt) pred))))

(define (max first . rest)
  (reduce (lambda (prev-max next) 
            (if (> next prev-max)
                next
                prev-max))
          first
          rest))

(define  (min first . rest) 
  (reduce (lambda (prev-min next)
            (if (< next prev-min)
                next
                prev-min))
          first
          rest))

(define (length lst)
  (reduce (lambda (acc-length next-elt)
            (+ 1 acc-length))
          0 
          lst))

(define (reverse lst)
  (reduce (lambda (prev next) 
            (cons next prev)) 
          '() 
          lst))

(define (map f lst)
  (reduce (lambda (x y)
            (cons (f x) y))
          '()
          lst))

(define (filter pred lst)
  (reduce (lambda (x y)
            (if (pred x) 
                (cons x y)
                y))
          '()
          lst))

(define (last lst)
  (reduce (lambda (x y)
            (if (null? y)
                x
                y))
          '()
          lst))

(define (find-with elem lst op)
  (case ((null? lst) '())
        ((eqv? elem (op (car lst))) (car lst))
        (else (find-with elem (cdr lst) op))))

(define (find elem lst) (find elem lst id))

(define (tuw-helper elem lst acc op)
  (case ((null? lst) (reverse acc))
        ((eqv? elem (op (car lst))) (reverse acc))
        (else (tuw-helper elem (cdr lst) (cons (car lst) acc) op))))

(define (take-until-with elem lst op)
  (tuw-helper elem lst '() op))

(define (take-until elem lst) (take-until-with elem lst id))

(define (drop-until-with elem lst op)
  (case ((null? lst) '())
        ((eqv? elem (op (car lst))) lst)
        (else (drop-until-with elem (cdr lst) op))))

(define (drop-until elem lst) (drop-until-with elem lst id))

(define (mem-helper pred op)
  (lambda (acc next)
    (if (and (not acc) (pred (op next)))
        next
        acc)))

(define (memq obj lst) 
  (reduce (mem-helper (curry eq? obj) id) #f lst))

(define (memv obj lst)
  (reduce (mem-helper (curry eqv? obj) id) #f lst))

(define member memv)

(define (ass-helper obj)
  (if (not obj) 
      '()
      (cdr obj)))

(define (member-with obj lst op)
  (reduce (mem-helper (curry eqv? obj) op) #f lst))

(define (assq obj alist) 
  (ass-helper (reduce (mem-helper (curry eq? obj) car) #f alist)))

(define (assv obj alist)
  (ass-helper (reduce (mem-helper (curry eqv? obj) car) #f alist)))

(define assoc assv)

(define (alist-keys alist)
          (if (null? alist)
              '()
               (cons (car (car alist))
                     (alist-keys (cdr alist)))))

(define (alist-values alist)
          (if (null? alist)
              '()
               (cons (cdr (car alist))
                     (alist-values (cdr alist)))))

(define first car)

(define second (compose car car))

(define third (compose car second))

"Welcome to SALSA!"